#==============================================================================
# ** Input Module Replacement
#==============================================================================
# The Ruby Devs
# Trimmed by Hawk-McKain
# Version 1.0
# 04.03.07 (dd.mm.yy)
#==============================================================================
# A read-only version of Ruby's built in Win32::Registry class.
#==============================================================================

#------------------------------------------------------------------------------
# * SDK Log Script
#------------------------------------------------------------------------------
SDK.log('Win32::Registry', 'Hawk-McKain', 1.0, '04.03.2007')

#------------------------------------------------------------------------------
# * Begin SDK Enable Test
#------------------------------------------------------------------------------
if SDK.enabled?('Win32::Registry')
  
  module Win32
    class Registry
      module Constants
        #These are all the constants needed for what we want.
        HKEY_CURRENT_USER = 0x80000001
        
        REG_BINARY = 3
        
        STANDARD_RIGHTS_READ = 0x00020000
        KEY_QUERY_VALUE = 0x0001
        KEY_ENUMERATE_SUB_KEYS = 0x0008
        KEY_NOTIFY = 0x0010
        KEY_READ = STANDARD_RIGHTS_READ |
          KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY
          
        REG_OPTION_RESERVED = 0x0000
        
        REG_OPENED_EXISTING_KEY = 2
              
        MAX_KEY_LENGTH = 514
        MAX_VALUE_LENGTH = 32768
      end
      include Constants
      include Enumerable
      
      #
      # Error
      #
      class Error < ::StandardError
        FormatMessageA = Win32API.new('kernel32.dll', 'FormatMessageA', 'LPLLPLP', 'L')
        def initialize(code)
          @code = code
          msg = "\0" * 1024
          len = FormatMessageA.call(0x1200, 0, code, 0, msg, 1024, 0)
          super msg[0, len].tr("\r", '').chomp
        end
        attr_reader :code
      end
      
      #
      # Predefined Keys
      #
      class PredefinedKey < Registry
        def initialize(hkey, keyname)
          @hkey = hkey
          @parent = nil
          @keyname = keyname
          @disposition = REG_OPENED_EXISTING_KEY
        end
        
        # Predefined keys cannot be closed
        def close
          raise Error.new(5) ## ERROR_ACCESS_DENIED
        end
        
        # Fake class for Registry#open, Registry#create
        def class
          Registry
        end
        
        # Make all
        Constants.constants.grep(/^HKEY_/) do |c|
          Registry.const_set c, new(Constants.const_get(c), c)
        end
      end
      
      #
      # Win32 APIs
      #
      module API
        [
          %w/RegOpenKeyExA    LPLLP        L/,
          %w/RegQueryValueExA LPLPPP       L/,
          %w/RegCloseKey      L            L/,
        ].each do |fn|
          const_set fn[0].intern, Win32API.new('advapi32.dll', *fn)
        end
        
        module_function
        
        def check(result)
          raise Error, result, caller(2) if result != 0
        end
        
        def packdw(dw)
          [dw].pack('V')
        end
        
        def unpackdw(dw)
          dw += [0].pack('V')
          dw.unpack('V')[0]
        end
              
        def OpenKey(hkey, name, opt, desired)
          result = packdw(0)
          check RegOpenKeyExA.call(hkey, name, opt, desired, result)
          unpackdw(result)
        end
                    
        def QueryValue(hkey, name)
          type = packdw(0)
          size = packdw(0)
          check RegQueryValueExA.call(hkey, name, 0, type, 0, size)
          data = ' ' * unpackdw(size)
          check RegQueryValueExA.call(hkey, name, 0, type, data, size)
          [ unpackdw(type), data[0, unpackdw(size)] ]
        end
        
        def CloseKey(hkey)
          check RegCloseKey.call(hkey)
        end
      end
          
      #
      # constructors
      #
      private_class_method :new
      
      def self.open(hkey, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED)
        subkey = subkey.chomp('\\')
        newkey = API.OpenKey(hkey.hkey, subkey, opt, desired)
        obj = new(newkey, hkey, subkey, REG_OPENED_EXISTING_KEY)
        if block_given?
          begin
            yield obj
          ensure
            obj.close
          end
        else
          obj
        end
      end
      
      #
      # finalizer
      #
      @@final = proc { |hkey| proc { API.CloseKey(hkey[0]) if hkey[0] } }
      
      #
      # initialize
      #
      def initialize(hkey, parent, keyname, disposition)
        @hkey = hkey
        @parent = parent
        @keyname = keyname
        @disposition = disposition
        @hkeyfinal = [ hkey ]
        ObjectSpace.define_finalizer self, @@final.call(@hkeyfinal)
      end
      attr_reader :hkey, :parent, :keyname, :disposition
      
      #
      # attributes
      #    
      def open?
        !@hkey.nil?
      end
      
      def name
        parent = self
        name = @keyname
        while parent = parent.parent
          name = parent.keyname + '\\' + name
        end
        name
      end
      
      def inspect
        "\#<Win32::Registry key=#{name.inspect}>"
      end
      
      #
      # marshalling
      #
      def _dump(depth)
        raise TypeError, "can't dump Win32::Registry"
      end
      
      #
      # open/close
      #
      def open(subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED, &blk)
        self.class.open(self, subkey, desired, opt, &blk)
      end
          
      def close
        API.CloseKey(@hkey)
        @hkey = @parent = @keyname = nil
        @hkeyfinal[0] = nil
      end
      
      #
      # reader
      #
      def read(name, *rtype)
        type, data = API.QueryValue(@hkey, name)
        unless rtype.empty? or rtype.include?(type)
          raise TypeError, "Type mismatch (expect #{rtype.inspect} but #{type} present)"
        end
        case type
        when REG_BINARY
          [ type, data ]
        else
          raise TypeError, "Type #{type} is not supported."
        end
      end
          
      def read_bin(name)
        read(name, REG_BINARY)[1]
      end
              
      %w[
        num_keys max_key_length
        num_values max_value_name_length max_value_length
        descriptor_length wtime
      ].each_with_index do |s, i|
        eval <<-__END__
          def #{s}
            info[#{i}]
          end
        __END__
      end
    end
  end

#------------------------------------------------------------------------------
# * End SDK Enable Test
#------------------------------------------------------------------------------
end